const OPTION = {
    REDO: Symbol(),
    UNDO: Symbol(),
    NONE: Symbol(),
};

export default function useRedo(source) {
    const undoBtn = ref({
        key: "undo",
        label: "撤销",
        onclick() {
            undo();
        },
    });
    const redoBtn = ref({
        key: "redo",
        label: "恢复",
        onclick() {
            redo();
        },
    });
    const undoStack = ref([]);
    const redoStack = ref([]);
    let canRedo = ref(false);
    let canUndo = ref(false);
    let option = OPTION.NONE;
    let sourceCopy = computed(() => {
        return JSON.parse(JSON.stringify(source.value));
    });
    watch(
        sourceCopy,
        (_, oldVal) => {
            if (option === OPTION.NONE) {
                redoStack.value = [];
            }
            if (option === OPTION.REDO || option === OPTION.UNDO) return;
            if (undoStack.value.length >= 50) {
                undoStack.value.shift();
            }
            undoStack.value.push(oldVal);
        },
        {
            deep: true,
        }
    );
    watchEffect(() => {
        canUndo.value = undoStack.value.length > 1;
        canRedo.value = redoStack.value.length !== 0;
        undoBtn.value.disabled = !canUndo.value;
        redoBtn.value.disabled = !canRedo.value;
    });
    document.addEventListener("keydown", undo);
    document.addEventListener("keydown", redo);
    onUnmounted(() => {
        document.removeEventListener("keydown", undo);
        document.removeEventListener("keydown", redo);
    });

    function undo(e) {
        if (!e || (e.ctrlKey && e.key === "z")) {
            if (canUndo.value) {
                option = OPTION.UNDO;
                redoStack.value.push(source.value);
                source.value = undoStack.value.pop();
                nextTick(() => {
                    option = OPTION.NONE;
                });
            }
        }
    }
    function redo(e) {
        if (!e || (e.ctrlKey && e.key === "y")) {
            if (canRedo.value) {
                option = OPTION.REDO;
                undoStack.value.push(source.value);
                source.value = redoStack.value.pop();
                nextTick(() => {
                    option = OPTION.NONE;
                });
            }
        }
    }
    return {
        undo,
        redo,
        canUndo,
        canRedo,
        undoBtn: undoBtn.value,
        redoBtn: redoBtn.value
    };
}
